Vue 
official::Vue.js 
text::Javascript 
挑战 | Vue.js挑战 (cn-vuejs-challenges.netlify.app) 
JS语法框架对比:JavaScript框架语法特性对比 (lainbo.com) 
 
 
一、Vue前置知识 1 命令 
在使用命令前先要安装Node.js
text::Node.js安装 
 
npm init vue@latest Project name >>  Add TypeScript?  Add JSX Support?  Add Vue Router for  Single Page Application development?  Add Pinia for  state management?  Add Vitest for  Unit testing?  Add an End-to-End Testing Solution?  Add ESLint for  code quality?  Add Prettier for  code formatting?  npm install cnpm install  npm run dev npm run build 
 
2 目录结构 
.vscode:vscode工具的配置文件
node_modules:Vue项目的运行依赖文件(npm install安装后生成的文件夹)
public:资源文件夹(浏览器图标)
src:源码文件夹
.gitignore:git忽略文件
index.html:入口HTML文件。所有文件都在这个html中运行。
package.json:信息描述文件
README.md:注释文件
vite.config.js: Vue配置文件
注:
一般情况下public放动态资源,src/assets中放静态资源。public可以是放服务器动态请求的资源,而assets是放前端必须要的资源。因为assets中的东西在build以后就编译到程序中,无法进行修改。 
 
 
<!DOCTYPE html > <html  lang ="en" >   <head >      <meta  charset ="UTF-8"  />      <link  rel ="icon"  href ="/favicon.ico"  />      <meta  name ="viewport"  content ="width=device-width, initial-scale=1.0"  />      <title > Vite App</title >    </head >    <body >           <div  id ="app" > </div >      <script  type ="module"  src ="/src/main.js" > </script >    </body >  </html > 
 
{   "name" :  "vue3_cli_default" ,     "version" :  "0.0.0" ,     "scripts" :  {      "dev" :  "vite" ,      "build" :  "vite build" ,      "serve" :  "vite preview"    } ,    "dependencies" :  {      "vue" :  "^3.2.8"     } ,    "devDependencies" :  {       "@vitejs/plugin-vue" :  "^1.6.0" ,      "@vue/compiler-sfc" :  "^3.2.6" ,      "vite" :  "^2.5.2"    }  } 
 
import  { defineConfig } from  'vite' import  vue from  '@vitejs/plugin-vue' export  default  defineConfig ({  plugins : [vue ()] }) 
 
3 API风格 
选项式API:Vue2写法。在组合式API的基础上实现。常用于项目中部分代码用Vue写。特点是在<script>中分别完善对应的功能后再选择,例如:data()、methods等。
组合式API:Vue3写法。常用于整个项目都用Vue写。特点是可以将功能整合起来。
 
<script> // 选项式API export default {   // data() 返回的属性将会成为响应式的状态   // 并且暴露在 this 上   data() {     return {       count: 0     }   },   // methods 是一些用来更改状态与触发更新的函数   // 它们可以在模板中作为事件处理器绑定   methods: {     increment() {       this.count++     }   },   // 生命周期钩子会在组件生命周期的各个不同阶段被调用   // 例如这个函数就会在组件挂载完成后被调用   mounted() {     console.log(`选项式API:The initial count is ${this.count}.`)   } } </script> <template>   <button @click="increment">选项式API:Count is: {{ count }}</button> </template> 
 
<script setup> // 组合式API import { ref, onMounted } from 'vue' // 响应式状态 const count = ref(0) // 用来修改状态、触发更新的函数 function increment() {   count.value++ } // 生命周期钩子 onMounted(() => {   console.log(`组合式API:The initial count is ${count.value}.`) }) </script> <template>   <button @click="increment">组合式API:Count is: {{ count }}</button> </template> 
 
 
二、Vue基础 
下列写法都是选项式写法。
 
1 文本插值 
script中的值会在template中直接显示出来。
每个绑定仅支持单一表达式,也就是一段能够被求值的JavaScript代码。一个简单的判断方法是是否可以合法地写在return后面。
注:在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成XSS 漏洞。请仅在内容安全可信时再使用 v-html,并且永远不要 使用用户提供的 HTML 内容。
 
<template> 	<h3>模板语法</h3> 	<p>{{ msg }}</p> 	<p>{{ hello }}</p> 	<p>{{ number + 1 }}</p> 	<p>{{ ok ? 'YES' : 'NO' }}</p> 	<p>{{ message.split('').reverse().join('') }}</p> </template> <script> export default{     data(){ 		return {             msg:"神奇的语法",             hello:"Hello World!",             number:10,             ok:true,             message:"大家好"         }     } } </script> <!-- 下列是无效语句 --> <!-- 这是一个语句,而非表达式 --> {{ var a = 1 }} <!-- 条件控制也不支持,请使用三元表达式 --> {{ if (ok) { return message } }} 
 
<!-- 链接解析 --> <template> 	<p>{{ rawHtml }}</p> <!-- 原样输出链接 --> 	<p v-html="rawHtml"></p> <!-- 解析后嵌套在p标签内 --> </template> <script> export default{     data(){ 		return {             rawHtml:"<a href='https://itbaizhan.com'>百战程序员</a>",         }     } } </script> 
 
2 属性绑定 
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个attribute,应该使用v-bind指令。
v-bind指令指示Vue将元素的idattribute 与组件的dynamicld属性保持一致。如果绑定的值是null或者undefined,那么该attribute将会从渲染的元素上移除。
因为v-bind非常常用,所以有简写语法,直接写个冒号即可。
 
<template> 	<div class="{{ msg }}">测试</div> <!-- class的值原样输出 --> 	<div v-bind:id="dynamicId" v-bind:class="msg">测试</div> <!-- 属性值转化后输出 --> 	<div v-bind:id="isNull" v-bind:class="isUndefined">测试</div> <!-- 属性值都被移除 --> 	<div :id="dynamicId" :class="msg">测试</div> <!-- 简写语法 --> 	<button :disabled="isButtonDisabled">不可点击</button> <!-- 可以动态改变按钮 --> 	<div v-bind="objectOfAttrs">测试</div> <!-- 一次绑定多个值 --> </template> <script> export default{     data(){ 		return {             msg:"active",             dynamicId:"appid",             isNull:null,             isUndefined:undefined,             isButtonDisabled:true,             objectOfAttrs:{                 id: "appId",  // 属性名:"属性值",可以自定义                  class: "appClass",                 myAttr: "attr"             }         }     } } </script> <!-- css会对类值为active的类生效 --> <style>     .active{ 	color: red;     font-size: 30px; } </style> 
 
3 条件渲染 
v-if,v-else-if,v-else:为真则渲染,否则不渲染。
v-show类似v-if,也是条件判断,但是v-show后面不可接else。
v-if和v-show:
v-if是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。 
v-if也是惰性的:如果在初次渲染时条件值为false,则不会做任何事。条件区块只有当条件首次变为true时才被渲染。 
相比之下,v-show简单许多,元素无论初始条件如何,始终会被渲染,只有CSS display属性会被切换。 
总的来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show较好;如果在运行时绑定条件很少改变,则v-if会更合适。 
 
 
<template> 	<div v-if="flag">can you see me?</div> <!-- 修改flag的值看不同结果 --> 	<div v-else>You should see me.</div> 	<div v-if="type === 'A'">A</div> <!-- 修改type的值看不同结果 --> 	<div v-else-if="type === 'B'">B</div> 	<div v-else-if="type === 'C'">C</div> 	<div v-else>Not A/B/C</div> 	<div v-show="flag">show!</div> <!-- 修改flag的值看不同结果 --> 	<div v-else>No show!</div> </template> <script> export default{     data(){ 		return {             flag:true,             type:"D"         }     } } </script> 
 
4 列表渲染 
我们可以使用v-for指令基于一个数组来渲染一个列表。v-for指令的值需要使用item in items形式的特殊语法,其中items是源数据的数组,而item是迭代项的别名。
列表有如下:
<p v-for="item in names">:依次获取names中的值存到item中。 
<p v-for="item of names">:同理,用of也行。 
<p v-for="(item,index) in names">:依次获取names中的值存到item中,获得下标存入index中。 
<p v-for="(item,index) of names">:同理。 
 
对象有如下:
<p v-for="(value,key,index) in userInfo">:依次获得值,键,下标。 
 
 
<template>     <div v-for="item in names">{{ item }}</div> 	<!-- 获得列表中对象值 -->     <div v-for="item in result">         <p>{{ item.title }}</p>         <img :src="item.avator" alt="" />     </div> 	<!-- 获得对象值 --> 	<p v-for="(value,key,index) in userInfo">{{ value }} - {{ key }} - {{ index }}</p> </template> <script> export default {     data() {         return {             names: ["uyi", "mio", "tsumugi"],             result: [                 {                     id: 1,                     title: '百度',                     avator: 'https://www.baidu.com/img/flexible/logo/pc/peak-result.png'                 },                 {                     id: 2,                     title: '淘宝',                     avator: 'https://gw.alicdn.com/mt/TB1ZnvPLFXXXXa3XFXXXXXXXXXX-63-63.png'                 }             ],             userInfo: {                 name: "ikun",                 age: 24,                 sex: "man"             }         }     } } </script> 
 
5 Key管理 
Vue默认按照”就地更新”的策略来更新通过v-for渲染的元素列表。当数据项的顺序改变时,Vue不会随之移动DOM元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
比如原始数据顺序是:1、2、3。当改成:1、3、2后,Vue不会仅仅改变两者的顺序,而是连带所有数据全部重新渲染,这样十分消耗性能。
为了给Vue一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的key attribute。
温馨提示:
key在这里是一个通过v-bind绑定的特殊attribute 
推荐在任何可行的时候为v-for提供一个key attribute 
key绑定的值期望是一个基础类型的值,例如字符串或number类型 
不建议使用index作为key的值,因为index是会随数组改变而改变,我们要确保每一条数据的唯一索引不会改变,可以使用数据库中返回的id作为索引。 
 
 
<template> 	<!-- 用id作为唯一key -->     <div v-for="(item,index) in result" :key="item.id">         <p>{{ item.title }}</p>         <img :src="item.avator" alt="" />     </div> </template> <script> export default {     data() {         return {             result: [                 {                     id: 1,                     title: '百度',                     avator: 'https://www.baidu.com/img/flexible/logo/pc/peak-result.png'                 },                 {                     id: 2,                     title: '淘宝',                     avator: 'https://gw.alicdn.com/mt/TB1ZnvPLFXXXXa3XFXXXXXXXXXX-63-63.png'                 }             ]         }     } } </script> 
 
6 事件管理 
我们可以使用v-on指令(简写为@)来监听DOM事件,并在事件触发时执行对应的JavaScript。用法:v-on:click="methodName"或@click="handler"
事件处理器的值可以是:
内联事件处理器:事件被触发时执行的内联JavaScript语句(与onclick类似)。通常用于简单场景。 
方法事件处理器:一个指向组件上定义的方法的属性名或是路径。 
 
 
<!-- 内联事件处理器 --> <template> 	<h3>内联事件处理器</h3> 	<button @click="count++">Inline add 1</button> 	<p>Count is: {{ count }}</p> </template> <script> export default {     data() {         return {           	count: 0         }     } } </script> <!-- 方法事件处理器 --> <template> 	<h3>方法事件处理器</h3> 	<button @click="addCount">Method add 1</button> 	<p>Count is: {{ count }}</p> </template> <script> export default {     data() {         return {           	count: 0         }     },     // 所有的方法或者函数都放在这里     methods:{         addCount(){             this.count++;         }     } } </script> 
 
7 事件传参 
事件参数可以获取event对象和通过事件传递数据。
 
<template> 	<!-- 无参传递事件 --> 	<button @click="addCount">Add</button> 	<p>{{ count }}</p> 	<!-- 有参传递事件 --> 	<p @click="getNameHandler($event,item)" v-for="(item,index) of names" :key="index">{{ item }}</p> </template> <script> export default {     data() {         return {           	count: 0,             names: ["uyi","mio","tsumugi"]         }     },     methods:{         // 无参可以直接获得e         addCount(e){             // 控制台输出             console.log(e.target.innerHTML = "A");             this.count++;         },         // 带参获得事件(点击名字可以输出对应结果)         getNameHandler(e,name)         { 			console.log(name);             console.log(e);         }     } } </script> 
 
8 事件修饰符 
在处理事件时调用event.preventDefault()或event.stopPropagation()是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理DOM事件的细节会更好。
official::更多修饰符 
因此Vue为v-on提供了事件修饰符,常见如下:
.stop:阻止事件冒泡 
.prevent:阻止默认事件 
.once:事件只会被触发一次 
.enter:回车事件触发的 
 
 
<template> 	<h3>点击但不跳转</h3> 	<a @click.prevent="clickHandle" href="https://www.baidu.com">百度</a> 	<h3>父元素不冒泡</h3> 	<!-- 阻止子元素向上冒泡到父元素 --> 	<div @click="clickDiv">     	<p @click.stop="clickP">测试冒泡</p>     </div> </template> <script> export default {     data() {         return {         }     },     methods:{         clickHandle(e){             console.log("被点了喵");         },         clickDiv(){             console.log("div被点了喵");         },         clickP(){             console.log("P被点了喵");         }     } } </script> 
 
9 数组侦测 
变更方法:
Vue能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:push()、pop()、shift()、unshift()、splice()、sort()、reverse() 
 
替换一个数组:
变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变(immutable)方法,例如flter(),concat()和slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的 
 
 
<template> 	<!-- 数组变化帧听 --> 	<button @click="addListHandle">添加数据</button> 	<ul>         <li v-for="(item,index) of names" :key="index">{{ item }}</li>     </ul> 	<!-- 合并数组 --> 	<button @click="concatHandle">合并数组</button> 	<h3>数组1</h3> 	<p v-for="(item,index) of nums1" :key="index">{{ item }}</p> 	<h3>数组2</h3> 	<p v-for="(item,index) of nums2" :key="index">{{ item }}</p> </template> <script> export default {     data() {         return {             names: ["uyi","mio","tsumugi"],             nums1: [1,2,3,4,5],             nums2: [6,7,8,9,10]         }     },     methods:{         addListHandle(){         	// UI会自动更新             // this.names.push("azi");            	// console.log(this.names);            	             // 不会引起UI自动更新             this.names.concat(["azi"]);             console.log(this.names.concat(["azi"])); 			 			// 重新赋值更新UI 			// this.names = this.names.concat(["azi"]); 			// console.log(this.names);         },        concatHandle(){            // 同理上面            this.nums1 = this.nums1.concat(this.nums2);        }     } } </script> 
 
10 计算属性 
模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。
重点区别:
 
<template> 	<h3>{{ itbaizhan.name }}</h3> 	<!-- 计算属性仅计算一次,不加括号 --> 	<p>{{ itbaizhanContent }}</p>     <p>{{ itbaizhanContent }}</p>     <p>{{ itbaizhanContent }}</p>     <p>{{ itbaizhanContent }}</p> 	<!-- 函数会被调用四次 -->     <p>{{ itbaizhanContents() }}</p>     <p>{{ itbaizhanContents() }}</p>     <p>{{ itbaizhanContents() }}</p>     <p>{{ itbaizhanContents() }}</p> </template> <script> export default {     data() {         return {             itbaizhan:{                 name: "百战程序员",                 content: ["前端","Java","Python"]             }         }     },     // 计算属性     computed:{         itbaizhanContent(){             return this.itbaizhan.content.length > 0 ? "Yes": "No";         }     },     // 方法     methods:{         itbaizhanContents(){             return this.itbaizhan.content.length > 0 ? "Yes": "No";         }     } } </script> 
 
11 Class绑定 
数据绑定的一个常见需求场景是操纵元素的CSS class列表,因为class是attribute,我们可以和其他attribute一样使用v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为class的v-bind用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。
在数组中也可以有条件地渲染某个class,使用三元表达式即可。
数组中也可以嵌套对象,但是对象中不能嵌套数组。
 
<template> 	<p :class="{'active': isActive,'text-danger': hasError}">Class样式绑定1</p> 	<!-- 可以直接绑定一个类 --> 	<p :class="classObject">Class样式绑定2</p> 	<!-- 可以绑定一个数组 --> 	<p :class="[arrActive,arrHasError]">Class样式绑定3</p> 	<!-- 有条件绑定 --> 	<p :class="[isActive ? 'active text-danger' : '']">Class样式绑定4</p> 	<!-- 数组嵌套对象 --> 	<p :class="[{ 'active':isActive }, errorClass]">Class样式绑定5</p> </template> <script> export default {     data(){         return{             // 改变bool值使类带上对应值             isActive:true,             hasError:false,             classObject:{             	'active':true,             	'text-danger':true         	},             arrActive:"active",             arrHasError:"text-danger", 			errorClass:"text-danger"         }     } } </script> <style> .active{ font-size: 30px; } .text-danger{ color: red; } </style> 
 
12 Style绑定 
数据绑定的一个常见需求场景是操纵元素的CSS style列表,因为style是attribute,我们可以和其他attribute一样使用v-bind将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为style的v-bind用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。
一般不使用,因为Style绑定的权重很高,后期想改比较麻烦,要使用推荐使用class绑定。
 
<template> 	<!-- 记得加单位 --> 	<p :style="{color:activeColor,fontSize:fontSize +'px' }">Style绑定1</p> 	<!-- 绑定类 --> 	<p :style="styleobject" >Style绑定2</p> 	<!-- 绑定数组(一般不用) -->	 	<p :style="[styleobject]">Style绑定3</p> </template> <script> export default { 	data(){ 		return{ 			activeColor: "green", 			fontSize: 30, 			styleobject: { 				color : "red", 				fontsize: "30px" 			} 		} 	} } </script> 
 
13 侦听器 
侦听数据的变化,然后执行对应操作。具有响应式的数据可以被侦听,比如{{ value }}。
我们可以使用watch选项在每次响应式属性发生变化时触发一个函数。
 
<template> 	<h3>侦听器</h3> 	<p>{{ message }}</p> 	<button @click="updateHandle">修改数据</button> </template> <script> export default { 	data(){ 		return{ 			message: "Hello" 		} 	}, 	methods: { 		updateHandle(){ 			this.message = "World"; 		} 	}, 	watch:{ 		// 与属性名相同(参数1是新数据,参数2是老数据) 		message(newValue,oldValue){ 			console.log(newValue,oldValue); 		} 	} } </script> 
 
14 表单输入绑定 
在前端处理表单时,我们常常需要将表单输入框的内容同步给JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦,v-model指令帮我们简化了这一步骤。
v-model也有修饰符
.lazy 默认情况下,v-model会在每次input事件后更新数据。你可以添加.lazy 修饰符来改为在每次change事件后更新数据。比如输入完成后点击搜索或其他地方才会更改数据。 
.number:只接受数字类型。 
.trim:输出会去除前后空格。 
 
 
<template> 	<h3>表单输入绑定</h3> 	<form>         <!-- 数据回显 --> 		数据:<input type="text" v-model="message">         <p>{{ message }}</p>         <!-- 懒惰回显-->         懒惰:<input type="text" v-model.lazy="lazy">         <p>{{ lazy }}</p>         <!-- 数字回显 --> 		数字:<input type="text" v-model.number="number">         <p>{{ number }}</p>         <!-- 去空格回显-->         左右去空格:<input type="text" v-model.trim="trim">         <p>{{ trim }}</p>         <!-- 勾选修改 -->         <input type="checkbox" id="checkbox" v-model="checked" />         <label for="checkbox">{{ checked }}</label> 	</form> </template> <script> export default { 	data(){ 		return{ 			message: "",             lazy: "",             number: "",             trim: "",             checked:false         }     } } </script> 
 
15 模板引用 
虽然Vue的声明性渲染模型为你抽象了大部分对DOM的直接操作,但在某些情况下,我们仍然需要直接访问底层DOM元素。要实现这一点,我们可以使用特殊的ref attribute。
挂载结束后引用都会被暴露在this.$refs之上。
对模板操作:
内容改变:{{ 模板语法 }} 
属性改变:v-bind:指令,:指令 
事件:v-on:click,@click 
DOM:ref="name",$refs.ref_name.js_func。如果没有特别的需求,不要操作DOM! 
 
 
<template> 	<!-- 绑定ref用于执行JS函数 --> 	<div ref="container" class="container">{{ content }}</div> 	<input ref="username" type="text" /> 	<button @click="getElementHandle">获取元素</button> </template> <script> export default { 	data(){ 		return{ 			content:"内容" 		} 	}, 	methods:{ 		getElementHandle(){ 		// 通过$refs.ref_name.js_func执行原生JS函数 		this.$refs.container.innerHTML = "哈哈哈"; 		console.log(this.$refs.username.value); 		} 	} } </script> 
 
 
三、 Vue组件 1 组件基础 
组件最大的优势就是可复用性。
当使用构建步骤时,我们一般会将Vue组件定义在一个单独的.vue文件中,这被叫做单文件组件(简称SFC)
在components中创建的Vue,通过App.vue进行关联。
 
<!-- 组件书写 --> <!-- 承载Html标签(必有) --> <temp1ate> 	<div>承载标签</div> </template> <!-- 承载JS逻辑(可以没有) --> <script> 	export default {} </script> <!-- 承载CSS样式(可以没有) --> <!-- scoped表示样式只作用于当前内部元素,不会影响别的组件 --> <style scoped> </style> 
 
<!-- 在App.vue中 --> <script setup>     // 第一步:引入组件     import MyComponent from "./components/MyComponent.vue"          export default {     // 第二步:注入组件     	components:{ 			MyComponent         }     } </script> <template> 	<!-- 第三步:显示组件,两种写法 -->     <MyComponent/> 	<my-component/ > </template> <style> </style> 
 
2 简单样式 
APP.vue中引入:Header、Main、Aside。
Main.vue中继续引入:Article。
Aside.vue中继续引入:Item。
 
<!-- Header.vue--> <template> 	<h3>Header</h3> </template> <style scoped> h3{ 	width: 100%;     height: 100px;     border: 5px solid #999;     text-align: center;     line-height: 100px;     box-sizing: border-box; } </style> 
 
<!-- Article.vue --> <template> 	<h3>Article</h3> </template> <style scoped> h3{     width: 80%;     margin: 0 auto;     text-align: center;     line-height: 100px;     box-sizing: border-box;     margin-top: 50px;     background: #999; } </style> 
 
<!-- Main.vue --> <template> 	<div class="main"> 		<h3>Main</h3>         <Article/>         <Article/>     </div> </template> <script> import Article from "./Article.vue" export default { 	components: { 		Article 	} } </script> <style scoped> .main{     float: left;     width: 70%;     height: 400px;     border: 5px solid #999;     box-sizing: border-box; } </style> 
 
<!-- Item.vue --> <template> 	<h3>Item</h3> </template> <style scoped> h3{     width: 80%;     margin: 0 auto;     text-align: center;     line-height: 100px;     box-sizing: border-box;     margin-top: 10px;     background: #999; } </style> 
 
<!-- Aside.vue --> <template> 	<div class="aside"> 		<h3>Aside</h3>         <Item />         <Item />         <Item />     </div> </template> <script> import Item from "./Item.vue" export default {     components: {         Item     } } </script> <style scoped> .aside{     float: right;     width: 30%;     height: 400px;     border: 5px solid #999;     box-sizing: border-box; } </style> 
 
<!-- App.vue --> <script> // This starter template is using Vue 3 <script setup> SFCs // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup     // 第一步:引入组件     import Header from "./components/Header.vue" 	import Main from "./components/Main.vue" 	import Aside from "./components/Aside.vue" 	 	export default { 	    // 第二步:注入组件 	    	components:{ 				Header, 				Main, 				Aside 	        } 	    } </script> <template> 	<Header /> 	<Main /> 	<Aside /> </template> 
 
3 注册方式 
全局注册虽然很方便,但有以下几个问题:
 
<script> import  Item  from  "./Item.vue" export  default  {    components : {         Item      } } </script> import  { createApp } from  'vue' import  App  from  './App.vue' import  Header  from  "./components/Header.vue" const  app = createApp (App )app.component ("Header" ,Header ) app.mount ('#app' ) 
 
4 组件传递数据 
组件与组件之间不是完全独立的,而是有交集的,那就是组件与组件之间是可以传递数据的传递数据的解决方案就是props。
通过props传递数据,不仅可以传递字符串类型的数据,还可以是其他类型,例如:数字、对象、数组等。但实际上任何类型的值都可以作为props的值被传递。
props传递数据,只能从父级传递到子级,不能反其道而行。
 
<!-- 父组件 --> <template> 	<h3>Parent</h3> 	<!-- 传递数据 --> 	<Child title="Parent数据" demo="测试" /> 	<!-- 传递动态数据 --> 	<Child :msg="message" :age="age" /> 	<!-- 传递数组数据 --> 	<Child :names="names" /> 	<!-- 传递对象数据 --> 	<!-- <Child :userInfo="userInfo" /> --> </template> <script> import Child from "./Child.vue" export default { 	data(){ 		return{ 			message:"Parent dynamic data!", 			age:20, 			names:["yui","mio","tsumugi"], 			userInfo:{ 				name: "kurumi", 				age: 16 			} 		}     }, 	components:{ 		Child     } } </script> <!-- 子组件 --> <!-- 没有的参数默认是传递undefined --> <template> 	<h3>Child</h3> 	<p>{{ title }}</p> 	<p>{{ demo }}</P> 	<p>{{ msg }}</P> 	<p>{{ age }}</P> 	<ul> 		<li v-for="(item, index) of names" :key="index">{{ item }}</li> 	</ul> 	<p>{{ userInfo.name }}</p> 	<p>{{ userInfo.age }}</p> </template> <script> export default { 	data(){ 		return{} 	},     // 子组件接收数据     props: ["title","demo","msg","age","names","userInfo"] } </script> 
 
5 组件数据验证 
添加组件数据验证:
type:类型验证 
default:默认值 
required:是否必须 
validator:自定义验证函数 
 
主:prop的数据是只读的,不可修改。
 
<!-- 父组件 --> <template> 	<h3>Parent</h3> 	<Child :single="single" :multiple="multiple"/> </template> <script> import Child from "./Child.vue" export default { 	data(){ 		return{ 			single: 20,             multiple: "123"         } 	}, 	components: { 		Child     } } </script> <!-- 子组件 --> <template> 	<h3>Child</h3> 	<p>{{ single }}</P> 	<p>{{ multiple }}</P> 	<p>{{ age }}</P> 	<p v-for="(item,index) of names" :key="index">{{ item }}</p> 	</template> <script> export default {     data() {         return {}     },     props: {         // 数据验证         single: {             // 类型验证,验证不通过爆警告             type: String,             // 必须项             required: true         },         multiple: {             // 多个类型验证             type: [Number, Array, Object]         },         age: {             type: Number,             // 默认值             default: 10         }, 		names: { 			type: Array,             // 数字可以字符串可以直接default,但是数组或对象必须用工厂函数返回默认值 			default(){ 				return ["yui","mio","tsumugi"] 			} 		}     } } </script> 
 
<!-- 验证类的同时还验证类中的数据 --> <script> export default {     props: {         value: {             type: Object,             required: true,             validator: (value) => {                 return typeof value.title === 'string' &&                     typeof value.content === 'string' &&                     typeof value.src === 'string' &&                     typeof value.time === 'number';             },         },     }, } </script> 
 
6 组件事件 
在组件的模板表达式中,可以直接使用$emit方法触发自定义事件。
触发自定义事件的目的是组件之间传递数据。
组件之间数据传递:
 
<!-- 父组件 --> <template>     <h3>组件事件</h3>     <Child @someEvent="getHandle" /> 	<p>父元素:{{ message }}</p> </template> <script> import Child from "./Child.vue" export default { 	data(){ 		return{ 			message: "" 		} 	},     components:{         Child     },     methods:{         getHandle(data){ 			this.message = data;         }     } } </script> <!-- 子组件 --> <template> 	<h3>Child</h3> 	<button @click="clickEventHandle">传递数据</button> </template> <script> export default { 	data(){ 		return{ 			msg: "Child数据!" 		} 	},     methods: {         clickEventHandle(){             //自定义事件             this.$emit("someEvent",this.msg)         }     } } </script> 
 
7 组件侦听 
组件配合v-model使用,可以实现一个组件读取数据,另一个组件接收数据。
 
<!-- 父组件 --> <template> 	<h3>Main</h3> 	<p>搜索内容为:{{ search }}</p> 	<Child @searchEvent="getSearch" /> </template> <script> import Child from "./Child.vue" export default {     data(){         return{             search: ""         }     },     components: {         Child     },     methods: {         getSearch(data){             this.search = data;         }     } } </script> <!-- 子组件 --> <template> 	搜索:<input type="text" v-model="search"> </template> <script> export default {     data(){         return{             search: ""         }     },     //侦听器     watch: {         search(newValue,oldValue){             this.$emit("searchEvent", newValue)         }     } } </script> 
 
8 props实现子传父 
props可以传递数据,因此可以利用子组件传递函数,在函数中携带参数,从而利用props实现子传父。
 
<!-- 父组件 --> <template> 	<h3>ComponentA</h3> 	<p>父元素:{{ message }}</p> 	<Child title="标题" :onEvent="dataFn"/> </template> <script> import Child from "./Child.vue" export default {     data(){         return{             message: ""         }     },     components: {         Child     },     methods: {         dataFn(data){             this.message = data;         }     } } </script> <!-- 子组件 --> <template> 	<h3>Child</h3> 	<p>{{ title }}</p> 	<!-- 类似回调函数 --> 	<p>{{ onEvent('传递数据')}}</p> </template> <script> export default {     data(){         return{}     },     props: {         title: String, 		onEvent: Function     } } </script> 
 
9 依赖注入 
正常情况下,如果祖父的数据要传递给孙子,需要先传递给父亲,再传递给孙子,这一问题成为”prop 逐级透传”。
provide和inject可以帮助我们解决这一问题。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
provide和inject只能由上到下传递,不能反向传递。
 
<!-- grandparent.vue --> <template> 	<h3>Grandparent</h3> 	<Parent/> </template> <script> import Parent from "./Parent.vue" export default {     data(){         return{ 			message: "Grandparent Data"         }     }, 	// 通过Data赋值     provide(){         return{             message: this.message         }     }, 	// 直接赋值     // provide:{ 	   //  message: "爷爷的财产"     // },     components:{         A     } } </script> <!-- parent.vue --> <template> 	<h3>Parent</h3> 	<Child /> </template> <script> import Child from "./child.vue" export default {     components: {         Child     }, } </script> <!-- child.vue --> <template> 	<h3>Child</h3> 	<p>{{ message }}</p> </template> <script> export default { 	inject: ["message"] } </script> 
 
import  { createApp } from  'vue' import  App  from  './App.vue' import  Header  from  "./components/Header.vue" const  app = createApp (App )app.provide (name,data) app.provide ("message" ,"I'm a message" ) app.mount ( '#app' ) 
 
10 插槽 
用于在组件之间传递模板。
<slot>元素是一个插槽出口(slot outlet),标示了父元素提供的插槽内容(slot content)将在哪里被渲染。
使用时,父元素模板定义子元素,在子元素中写模板。子元素使用<slot>双标签调用出父元素的定义的内容。
插槽中可以书写动态数据,动态数据是存储在父组件中的。
插槽可以有默认值,当父组件的插槽中内容为空时,将显示子组件中插槽标签中的内容。
具名插槽:给插槽一个属性值,用于区分不同的插槽。写法是<template v-slot:name>,可以简写成<template #name>
 
<!-- 父组件 --> <template> 	<!-- 父元素中的子元素标签用双标签,在标签中写模板 --> 	<Child>     	<div>             <h3>slot title</h3>             <p>slot content</p>             <p>{{ content }}</p>     	</div>     </Child> 	<!-- 通过名字区分插槽 --> 	<Child> 		<template v-slot:first> 			<h3> I'm first </h3> 		</template> 		<template #second> 			<h3> I'm second</h3> 		</template> 	</Child> 	<!-- 使用父子组件的数据,:name="别名",然后点出里面的属性 -->     <Child v-slot:second="childData">         <h3>{{ message }} -- {{ childData.msg }}</h3>     </Child> </template> <script> import Child from "./Child.vue" export default {     data(){         return{             content: "Hello World!",             message: "Parent Data!"         }     },     components: {         Child     }, } </script> <!-- 子组件 --> <template> 	<h3>Child</h3> 	<!-- 使用插槽。注:标签名不能改-->	     <!-- 将父组件中插槽中的内容注释后,将显示下列默认内容 --> 	<slot>无name默认值</slot> 	<!-- 用name指向父组件中v-slot指向的模板 --> 	<slot name="first">first默认值</slot> 	<!-- 传递msg数据给父组件 --> 	<slot name="second" :msg="childMessage">second默认值</slot> </template> <script> export default{     data(){         return{             childMessage:"Child Data!"         }     } } </script> 
 
11 组件生命周期 
生命周期函数
创建期:beforeCreate、created 
挂载期:beforeMount、mounted 
更新期:beforeUpdate、updated 
销毁期:beforeUnmount、unmounted 
 
当组件进入对应的时期后,将执行对应的函数的内容。
 
<template> 	<h3>组件生命周期</h3> 	<p>{{ message }}</p> 	<button @click="updateHandle">更新数据</button> </template> <script> export default { 	data(){ 		return{ 			message: "老数据" 		} 	}, 	methods: { 		updateHandle(){ 			this.message="新数据" 		} 	}, 	beforeCreate(){ 		console.log("组件创建之前"); 	}, 	created(){ 		console.log("组件创建之后"); 	}, 	beforeMount(){ 		console.log("组件渲染之前"); 	}, 	mounted(){ 		console.log("组件创建之后"); 	}, 	beforeupdate(){ 		console.log("数据更新之前"); 	}, 	updated(){ 		console.log("数据更新之后"); 	}, 	beforeUnmount(){ 		console.log("组件卸载之前"); 	}, 	unmounted(){ 		console.log("组件卸载之后"); 	} } </script> 
 
12 生命周期应用 
常见应用:
通过ref获取元素DOM结构。在beforeMount()前无法获得,因为组件未挂载。在mounted()后可以操作DOM结构,此时组件挂载。 
模拟网络请求渲染数据。在beforeCreate()前无法获得,因为组件未创建,没有data(),数据无法存储。在created()后则可以将获得的数据存储在data()中。但是实际上为了让网页先显示结构再显示数据,所以一般是在mounted()中作数据获得的操作,因为先获得数据没有结构也没有实际作用。 
 
 
<!-- 读取DOM结构 --> <template> 	<h3>组件生命周期函数应用</h3> 	<p ref="name">百战程序员</p> </template> <script> export default { 	beforeMount(){         // 此时组件未挂载,获得undefined 		console.log(this.$refs.name);  	}, 	mounted(){         // 此时组件挂载,获得百战程序员 		console.log(this.$refs.name); 	} } </script> <!-- 模拟网络请求渲染数据 --> <!-- 结果只有mio,因为在创建之前没有banner,banner还是undefined --> <template> 	<ul>         <li v-for="(item,index) of banner" :key="index">             <h3>{{ item.title }}</h3>             <p>{{ item.content }}</p>     	</li>     </ul> </template> <script> export default {     data(){         return{             banner: []         }     }, 	beforeCreate(){         // 模拟网络获得数据 		this.banner = [ 		{ 			title: "uyi", 			content: "guitar" 		} 		] 	},     created(){         // 模拟网络获得数据         this.banner.push(         {             title: "mio",             content: "bass"         }         )     },     // 为了让网页先显示结构再显示数据,所以一般是在mounted中作数据获得的操作     mounted(){         this.banner.push(         {             title: "tsumugi",             content: "keyboard"         }         )     } } </script> 
 
13 组件切换 
有些场景会需要在两个或多个场景之间来回切换,比如Tab界面。
当使用<component is="...">来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过<keep-alive>组件强制被切换掉的组件仍然保持“存活”的状态。
在下列案例中,如果不加上<keep-alive>,切换组件时,组件会被卸载,这样更新了数据后再切换数据也会重置。但是加上<keep-alive>后组件将不会卸载,数据也就能保存下来。
 
<!-- Parent.vue --> <template> 	<!-- keep-alive必须包裹确定的组件,不能再包裹button,否则报错 --> 	<keep-alive>         <!-- 加载组件,is绑定 --> 		<component :is="tabComponent"></component> 	</keep-alive> 	<button @click="changeHandle">切换组件</button> </template> <script> import A from "./A.vue" import B from "./B.vue" export default { 	data(){ 		return{             // 组件名绑定,要用字符串 			tabComponent : "A", 		} 	}, 	components:{A,B}, 	methods:{ 		changeHandle(){             // 切换组件,要用字符串 			this.tabComponent = this.tabComponent == "A" ? "B" : "A" 			} 	}, } </script> <!-- A.vue --> <template> 	<h3>我是A</h3> 	<p>{{ message }}</p> 	<button @click="updateHandle">更新数据</button> </template> <script> export default { 	data(){ 		return{ 			message: "老数据"         }     },     beforeUnmount(){         console.log("组件卸载之前");     },     unmounted(){         console.log("组件卸载之后");     },     methods: {         updateHandle(){             this.message = "新数据"         }     } } </script> <!-- B.vue --> <template> 	<p>我是B</p> </template> <script> export default { 	data(){ 		return{ 		} 	},     beforeUnmount(){ 		console.log("组件卸载之前"); 	}, 	unmounted(){ 		console.log("组件卸载之后"); 	} } </script> 
 
14 异步组件 
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue提供了defineAsyncComponent方法来实现此功能。
下列案例中,若不做成异步,则在网络请求中会一次性加载A,B。若将B做成了异步请求,则一开始并不会请求B,当用户点击切换后才会在网络中请求B。请求后将保存在本地,多次切换后都不会再发送网络请求。
 
<!-- Parent.vue。A.vue和B.vue在上一节 --> <template> 	<component :is="tabComponent"></component> 	<button @click="changeHandle">切换组件</button> </template> <script> // 导入组件 import { defineAsyncComponent } from 'vue' import A from "./A.vue" // 设置成异步 const B = defineAsyncComponent(() => import('./B.vue')) export default { 	data(){ 		return{             // 组件名绑定,要用字符串 			tabComponent : "A", 		} 	}, 	components:{A,B}, 	methods:{ 		changeHandle(){             // 切换组件,要用字符串 			this.tabComponent = this.tabComponent == "A" ? "B" : "A" 			} 	}, } </script> 
 
 
四、Vue其他 1 透传 Attributes 
“透传attribute”指的是传递给一个组件,却没有被该组件声明为props或emits的attribute或者v-on事件监听器。最常见的例子就是class、style和id。当一个组件以单个元素为根作渲染时,透传的attribute会自动被添加到根元素上。
注:在实际场景中并不常用。
 
<!-- 父组件 --> <template> 	<Child class="attr-container" /> </template> <script> import Child from "./Child.vue" export default {     components: {         Child     } } </script> <!-- 子组件 --> <template> 	<!-- 必须是唯一根元素 --> 	<h3>透传属性</h3> 	<!-- <p>再多加一个标签就会失效</p> --> </template> <script> // 禁用属性继承,添加后class将不会继承下来 // export default{ //    inheritAttrs: false // } </script> <style> .attr-container{     color: red; } </style> 
 
2 引入第三方库 2.1 Swiper 
常用动画库:offifcial::Swiper Vue.js 
首先:npm i swiper进行安装
 
<!-- 导航+轮播图案例 --> <template> 	<!-- 导航栏属性设置 --> 	<swiper :navigation="true" :modules="modules" class="mySwiper">         <!-- 单个栏目设置 -->     	<swiper-slide > 			<div class="slide slide1"></div> 		</swiper-slide>     	<swiper-slide > 			<div class="slide slide2"></div> 		</swiper-slide> 		<swiper-slide > 			<div class="slide slide3"></div> 		</swiper-slide> 		<swiper-slide > 			<div class="slide slide4"></div> 		</swiper-slide> 		<swiper-slide > 			<div class="slide slide5"></div> 		</swiper-slide>   	</swiper> </template> <script> // 导入轮播图 import { Swiper, SwiperSlide } from 'swiper/vue'; // 导入导航栏 import { Navigation } from 'swiper/modules'; // 导入对应css import 'swiper/css'; import 'swiper/css/navigation'; export default {     // 导航栏注册 	setup() {         return {             modules: [Navigation],         } 	},     // 轮播图注册 	components: { 		Swiper, 		SwiperSlide, 	}, }; </script> <style scoped> .swiper-slide { 	height: 405px; 	display: flex; 	align-items: center; 	justify-content: center; } /* 切割图片 */ .slide{ 	height: 405px; 	width: 144px; } .slide1 { 	background: url("src/assets/img/mygo.png") no-repeat 0px 0px; } .slide2 { 	background: url("src/assets/img/mygo.png") no-repeat -144px 0px;  } .slide3 { 	background: url("src/assets/img/mygo.png") no-repeat -288px 0px; } .slide4 { 	background: url("src/assets/img/mygo.png") no-repeat -432px 0px;  } .slide5 { 	background: url("src/assets/img/mygo.png") no-repeat -576px 0px;  } </style> 
 
3 Vuex 
Vuex是一个专为Vue.js应用程序开发的状态管理模式+库 。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说,状态管理可以理解成为了更方便的管理组件之间的数据交互,提供了一个集中式的管理方案,任何组件都可以按照指定的方式进行读取和改变数据。
最常用的核心概念包含:
state:存储数据 
Getter:对数据进行过滤 
Mutation:修改数据 
Action:类似Mutation,但是Action是提交Mutation,不是变更状态,并且Action可以包含任意异步操作 
 
official::Vuex (vuejs.org) 
安装:npm i vuex
新建文件:src/store/index.js
 
import  { createStore } from  "vuex" export  default  store = createStore ({         state : {                  counter : 0 ;     },          getters : {         getCount (state ){             return  state.counter  > 0  ? state.counter  : "counter小于0,不符合要求"          }     },          mutatons : {         addCounter (state, num ){             state.counter  += num;         }     },          actions : {         asyncAddCounter ({ commit } ){           	axios.get ("http://iwenwiki.com/api/generator/list.php" )         	.then (res  =>  {             commit ("addCounter" , res.data [0 ])         })         }     } }) 
 
import  store from  "./store" createApp (App ).use (store).mount ("#app" );
 
<!-- 直接在组件中进行使用 --> <!-- 读取方法1:用于单次 --> <template> 	<p>counter = {{ $store.state.counter }}</p> 	<p>counter = {{ $store.state.getCount }}</p> 	<button @click="addClickHandle">增加</button> 	<button @click="addAsyncClickHandle">异步增加</button> </template> <script> export default{     methods: {         addClickHandle(){             // 固定调用方式             this.$store.commit("addCounter", 10);         },         addAsyncClickHandle(){             this.$store.dispatch("asyncAddCounter");         }     } } </script> <!-- 读取方法2:用于多次 --> <template> 	<p>counter = {{ counter }}</p> 	<p>counter = {{ getCounter }}</p>     <button @click="addClickHandle">增加</button> </template> <script> import { mapState, mapGetters, mapMutations } from "vuex" export default{     computed: {         // 专门来读取vuex的数据         ...mapState(["counter"]),         ...mapGetters(["getCounter"])     },     methods: {         ...mapMutations(["addCounter"]),         addClickHandle(){             this.addCounter(20);         },         addAsyncClickHandle(){             this.asyncAddCounter();         }     } }      </script> 
 
4 Pinia 
由于 Pinia 在生态系统中能够承担相同的职责且能做得更好,因此 Vuex 现在处于维护模式。对于新的应用,建议使用 Pinia。
official::Pinia (vuejs.org) 
安装:npm i pinia
新建:src/stores/index.js
使用:
State:属性 
Method:方法 
Getter:基于 store 中 state 创建的计算属性,它返回的是派生自 state 的值,并且会根据 state 的变化自动更新 
Action:用于修改 state 或执行副作用(如异步操作)的方法。在 Pinia 中,可以直接在 action 内部修改 state,而无需通过 mutation。 
 
 
import  { createApp } from  'vue' import  { createPinia } from  'pinia' import  App  from  './App.vue' const  app = createApp (App );app.use (createPinia ()); app.mount ('#app' ); 
 
import  { defineStore } from  'pinia' import  { ref } from  'vue' export  const  useCsrfTokenStore = defineStore ('csrf' ,() =>  {	     const  csrfToken = ref ('' ); 	      	const  get  = ( ) => { return  csrfToken.value ; }; 	 	const  set  = (token ) => { csrfToken.value  = token;};               const  tokenIsNotEmpty = computed (() =>  !!csrfToken.value );      	     async  fetchAndSetToken ( ) {        const  response = await  axios.get ('/api/csrf-token' );        set (response.data .token );     } 	      	return  { get, set, tokenIsNotEmpty, fetchAndSetToken }; }) 
 
<!-- 使用 --> <template>   <div>     <button @click="fetchAndSetToken">Fetch and Set Token</button>     <p v-if="tokenIsEmpty">No token fetched yet.</p>     <p v-else>Current token: {{ csrfToken }}</p>   </div> </template> <script setup> import { useCsrfTokenStore } from '@/stores/csrf'; const csrfTokenStore = useCsrfTokenStore(); // 直接使用 store 的 state 和 methods const { csrfToken, tokenIsEmpty, fetchAndSetToken } = csrfTokenStore; </script> 
 
 
五、Vue网络 1 Vue结构 
应用实例:每个Vue应用都是通过createApp函数创建一个新的应用实例。
根组件:我们传入createApp的对象实际上是一个组件,每个应用都需要一个”根组件”,其他组件将作为其子组件。
挂载应用:应用实例必须在调用了.mount()方法后才会渲染出来。该方法接收一个”容器”参数,可以是一个实际的DOM元素或是一个CSS选择器字符串。
src/assets:存放css,图片等资源。
 
<!-- App .vue  --> import  { createApp } from  'vue' import  App  from  './App.vue' const  app = createApp (App )mount ('#app' )
 
<body >          <div  id ="app" > </div >           <script  type ="module"  src ="/src/main.js" > </script >  </body > 
 
2 main.js 
vue代码最后都会被编译成main.js,然后引入main.js执行代码。
 
import  { createApp } from  'vue' import  App  from  './App.vue' const  app = createApp (App );app.config .globalProperties .$serverIP  = 'localhost:8000' ; app.mount ('#app' ) 
 
 <script> // any.vue export default {   created() {     // 使用全局变量     console.log(this.$serverIP);    } }; </script> 
 
3 axios 
基于promise网络请求库,作用于node.js和浏览器中。
安装:npm i axios
文档:official::Axios中文文档 
 
4 路由配置 
安装:
路径创建:
/src/api/index.js:访问网页 
/src/api/path.js:网络路径 
/src/config/conster.js:本地网址管理 
/src/utils/request.js:网络请求处理 
 
 
import  axios from  "../utils/request.js" import  path from  "./path.js" const  api = {	getIndex ( ){ 		return  axios.get (path.baseUrl  + path.suffix ); 	} } export  default  api;
 
const  base = {	baseUrl : "localhost:8000" , 	suffix : "/iAmIn.html"  } export  default  base;
 
let  module  = {	 	dev : 'http://127.0.0.1:5173' , 	 	test : '' , 	 	pro : ''  } export  const  BASE_URL  = module .dev 
 
import  axios from  "axios" import  qs from  'qs' ;import  { BASE_URL  } from  '/src/config/conster.js' const  instance = axios.create ({    baseURL : BASE_URL , 	timeout : 5000  }) instance.interceptors .request .use ( 	config  =>  { 		 		if (config.method  === "post" ){ 			config.data  = qs.stringify (config.data ); 		} 		 		 		 		return  config; 	}, 	error  =>  { 		return  Promise .reject (error); 	} ) const  errorHandle  = (status,info ) =>{	switch (status){ 		case  400 : 			console .log ("语义有误" );break ; 		case  401 : 			console .log ("服务器认证失败" );break ; 		case  403 : 			console .log ("服务器拒绝访问" );break ; 		case  404 : 			console .log ("地址错误" );break ; 		case  500 : 			console .log ("服务器遇到意外" );break ; 		case  502 : 			console .log ("服务器无响应" );break  ; 		default : 			console .log (info);break ; 	} } instance.interceptors .response .use ( 	response  =>  { 		NProgress .done (); 		return  response.status  === 200  ? Promise .resolve (response) : Promise .reject (response); 	}, 	error  =>  { 		NProgress .done (); 		 		const  { response } = error; 		 		errorHandle (response.status , response.info ); 	} ) export  default  instance;
 
<!-- 使用api --> <script> import api from "../api/index.js" export default{     mounted(){         api.getIndex().then(res =>{         	console.log(res.data);         })     } } </script> 
 
5 跨域问题 
JS采取的是同源策略。
同源策略是浏览器的一项安全策略,浏览器只允许js代码请求和当前所在服务器域名,端口,协议相同的数据接口上的数据,这就是同源策略。
也就是说,当协议、域名、端口 任意一个不相同时,都会产生跨域问题,所以又应该如何解决跨域问题呢。
跨域常见异常:
Access to XMLHttpRequest at "url" from origin "url" has been blocked by CORS policy: No 'Access-Control-Allow-Origin’ header is present on the requested resource. 
GETnet::ERR_FAILED 200 
Uncaught (in promise) 
 
后台跨域:cors
前台跨域:proxy
解决完跨域配置之后,要重启服务器才有用。 
注:配置代理的时候'/api'如果直接写成'/',这样表示http://127.0.0.1:80/后面的路由都进行代理,这样会导致你加载本地资源会出错,因为你把加载本地资源的路径全部都代理到服务端去了,浏览器会向服务器进行资源请求,这样就会导致页面报错。
切记 :本地同时前后端联合调试的时候,目标请求地址不能用 localhost,必须改成 127.0.0.1,否则会报错:http proxy error: Error: connect ECONNREFUSED ::1:8000。
 
export  default  defineConfig ({    plugins : [vue ()],      	server : {         https : true ,  		proxy : {                        	 			'/api' : { 				target : '<url>' ,   				changeOrigin : true ,                  ws : true ,   				rewrite : (path ) =>  path.replace (/^\/api/ , '' )  			} 		} 	}, }) 
 
6 页面跳转 
在Vue中,我们可以通过vue-router路由管理页面之间的关系。
Vue Router是Vue.js的官方路由。它与Vue.js核心深度集成,让用Vue.js构建单页应用变得轻而易举。
安装:npm i vue-router
路径创建:
/src/router/index.js:路由配置文件 
/src/views/name.vue:路由对应的文件 
 
histroy的参数:
createwebHashHistory():显示出的网址如下:home:http://localhost : 8080/#/,about:http://localhost:8080/# / about。原理是a标签锚点连接。 
createwebHistory():显示出的网址如下:home:http://localhost:8080,about:http://localhost:8080/ about。此种方式,需要后台配合做重定向,否则会出现404问题。原理是H5的pushState() 
 
createWebHashHistory使用URL的哈希部分(如example.com/#/path)作为路径,这种模式下,浏览器不会发送实际的请求,而是通过监听URL的变化来实现前端路由的跳转。这种模式的优点是兼容性好,甚至可以在不支持HTML5历史模式的浏览器中正常使用,但URL中的哈希部分可能不太美观。
createWebHistory使用真实的URL路径(如example.com/path),需要服务器的支持,通过HTML5的history API来实现前端路由的跳转。这种模式的优点是URL更加美观,没有哈希部分,但兼容性可能稍差,需要服务器的配置支持。
需要注意的是,createWebHistory只有在服务器配置正确的情况下才能正常工作,而createWebHashHistory则不需要服务器配置。因此,在选择使用哪种创建函数时,需要考虑项目的需求以及服务器的配置情况。
 
import  { createRouter, createWebHashHistory } from  'vue-router' import  HomeView  from  '../views/index.vue' const  routes = [	{          		path : '/' ,          		name : 'home' ,          		component : HomeView  	}, 	{ 		path : '/about' , 		name : 'about' ,          		component : () =>  import ('../views/about.vue' ) 	} ] const  router = createRouter ({	history : createWebHashHistory (), 	routes }) export  default  router;
 
<!-- App.vue --> <template> 	<!-- 设置路由跳转 --> 	<router-link to="/">首页</router-link> 	<router-link to="/about">关于</router-link> 	<!-- 显示路由 --> 	<router-view></router-view> </template> 
 
import  { createApp } from  'vue' import  App  from  './App.vue' import  router from  './router' createApp (App ).use (router).mount ('#app' )
 
7 路由带参 
在路由中携带参数。常见业务是页面中存在列表项,然后点击每个列表项查看详情。
 
{          path : "/list/:name[/:other...]" ,     name : "list" ,     component : () =>  import ("../views/ListView.vue" ) } 
 
<!-- vue中带参 --> <li><router-link to="/list/内蒙">内蒙旅游十大景区</router-link></1i> <li><router-link to="/list/北京">北京旅游十大景区</router-link></li> <li><router-link to="/list/四川">四川旅游十大景区</router-link></li> 
 
<!-- 详细界面中使用参数 --> <p> {{ $route.params.name}} 城市旅游景区详情</p> 
 
8 路由嵌套 
页面中继续有页面选项。
 
const  routes = [    {         path : '/about' ,         name : 'about' ,                  redirect : "/about/us" ,         component : () =>  import ("../views/AboutView.vue" ),         children :[             {                                  path : "us" ,                 component : () =>  import ("../views/AboutSub/AboutUS.vue" )             },             {                 path : "info" ,                 component : () =>  import ("../views/AboutSub/AboutInfo.vu e" )             }         ]     } ] 
 
<!-- vue中使用 --> <template> 	<div class="about">     <!-- 直接写路径即可 --> 	<router-link to="/about/us">关于我们</router-link>      <router-link to="/about/info">关于信息</router-link>     <router-view></router-view> </template> 
 
 
六、Vue3 1 升级 
六大亮点:
Performance:性能更比Vue 2.0强。 
Tree shaking support:可以将无用模块”剪辑“,仅打包需要的。 
composition API:组合API 
Fragment, Teleport, Suspense:”碎片“,Teleport即Protal传送门,”悬念“ 
Better TypeScript support:更优秀的Ts支持(Vue3的底层是Ts) 
Custom Renderer APl:暴露了自定义渲染API 
 
 
2 组合式API 
Vue3组合了API,在setup()中,可以定义数据、方法、props和context等。
在2.x中,组件的方法中可以通过this获取到当前组件的实例,并执行data变量的修改,方法的调用,组件的通信等等,但是在3.x中,setup()在beforeCreate和created时机就已调用,无法使用和2.x一样的this。但是可以通过接收setup(props,ctx)的方法,获取到当前组件的实例和props。总之,在setup中,this指向的是undefined
 
<template> 	<div class="hello"> 		<p>{{ message }}</p> 		<ul> 			<li v-for="(item,index) in names.list" :key="index">{{ item }}</li> 		</ul> 		<button @click="clickHandle">按钮</button> 		<p>{{ msg }}</p> 	</div> </template> <script> import { ref, reactive } from "vue" export default { 	name:'Helloworld', 	// 定义props接收数据 	props:{ 		msg:String 	}, 	//组合式API(props传参,ctx表示this) 	setup(props,ctx) {  		// ref:定义简单数据 		const message = ref("我是消息") 		//reactive:定义复杂数据(数组、对象) 		const names = reactive({ 		list: [ "yui", "mio" , "tsumugi"], 		}) 		 		// 定义函数 		function clickHandle(){ 			message.value="我是新的消息"; 		} 		 		// props赋值 		const msg = props.msg; 		 		// 查看ctx的数据(包括attrs、emit、slots等) 		console.log(ctx); 		 		// 定义的数据需要返回出去才能被使用 		return{ 			message, 			names, 			// 函数也需要返回 			clickHandle, 			msg 			} 		} 	} </script> 
 
<!-- 直接将setup写到标签中,不用return --> <template>   <div class="hello">     <p>{{ message }}</p>     <ul>       <li v-for="(item, index) in names.list" :key="index">{{ item }}</li>     </ul>     <button @click="clickHandle">按钮</button>     <p>{{ msg }}</p>   </div> </template> <script setup> import { ref, reactive } from "vue" // 获取props数据 const props = defineProps({   msg: String }) // ref:定义简单数据 const message = ref("我是消息") // reactive:定义复杂数据(数组、对象) const names = reactive({   list: ["yui", "mio", "tsumugi"], }) // 定义函数 function clickHandle() {   message.value = "我是新的消息" } // props赋值 const msg = props.msg </script> 
 
3 setup使用生命周期函数 
Options API 
Hook inside setup 
 
 
beforeCreate 
Not needed* 
 
created 
Not needed* 
 
beforeMount 
onBeforeMount 
 
mounted 
onMounted 
 
beforeUpdate 
onBeforeUpdate 
 
updated 
onUpdated 
 
beforeUnmount 
onBeforeUnmount 
 
unmounted 
onUnmounted 
 
 
 
<template> 	<div class="hello"></div> </template> <script setup> import { onMounted } from "vue"; // 比以前有优势,以前同一个生命周期函数只能存在一个,现在可以存在多个 onMounted(() =>{ console.log("生命周期函数:onMounted1");}); onMounted(() =>{ console.log("生命周期函数:onMounted2");}); </script> 
 
4 依赖注入 
Provide / lnject:
provide()和inject()可以实现嵌套组件之间的数据传递。 
这两个函数只能在setup()函数中使用。 
父级组件中使用provide()函数向下传递数据。 
子级组件中使用inject()获取上层传递过来的数据。 
不限层级。 
 
 
<!-- 父组件的script --> <script setup> import { provide } from "vue" proivde("customVal", "我是父组件向子组件传递的值"); </script> <!-- 子组件的script --> <script setup> import { inject } from "vue" const customVal = inject("customVal"); </script> 
 
七、element-plus 1 基础使用 
css组件库
official::Element Plus (element-plus.org) 
必须安装:npm i element-plus
按需引入额外安装:
npm i unplugin-vue-components 
npm i unplugin-auto-import 
 
 
import  { createApp } from  'vue' import  ElementPlus  from  'element-plus' import  'element-plus/dist/index.css' import  App  from  './App.vue' const  app = createApp (App )app.use (ElementPlus ) app.mount ( ' #app ' ) 
 
import  { defineConfig } from  'vite' import  AutoImport  from  'unplugin-auto-import/vite' import  Components  from  'unplugin-vue-components/vite' import  { ElementPlusResolver  } from  'unplugin-vue-components/resolvers' export  default  defineConfig ({     plugins : [          AutoImport ({       resolvers : [ElementPlusResolver ()],     }),     Components ({       resolvers : [ElementPlusResolver ()],     }),   ], }) 
 
<template>   <el-row class="mb-4">     <el-button>Default</el-button>     <el-button type="primary">Primary</el-button>     <el-button type="success">Success</el-button>     <el-button type="info">Info</el-button>     <el-button type="warning">Warning</el-button>     <el-button type="danger">Danger</el-button>   </el-row> </template> 
 
2 字体处理 
字体使用需要额外安装。
安装:npm i @element-plus/icons-vue
创建文件:src/plugins/icons.js
 
import  * as  components from  "@element-plus/icons-vue" ;export  default  {	install : (app ) =>  { 		for  (const  key in  components) { 			const  componentconfig = components[key]; 			app.component (componentconfig.name ,componentconfig);         }     }, }; 
 
import  elementIcon from  "./plugins/icons" app.use (elementIcon) 
 
<!-- 使用图标 --> <el-icon class="expand" color="#409EFC" :size="30"> 	<expand /> </el-icon> 
 
 
八、其他插件 1 顶端加载条 
在顶端新增一个路由加载条,可以看见网页的加载进度。
安装:npm i nprogress -S
 
import  NProgress  from  'nprogress' import  'nprogress/nprogress.css' router.beforeEach ((to,from ,next))=>{ 	NProgress .start (); 	next (); } router.afterEach ((to,from ))=>{ 	NProgress .done (); } 
 
 
九、其他问题 1 网络 
Q1:GET http://127.0.0.1:5173/api/mainPage/login.html net::ERR_CONNECTION_REFUSED
A1:只能用localhost访问,否则修改package.json——scripts——dev——vite --host IP来新增IP。
 
2 访问类中元素 
Q1:this.id无法访问到id值。
A1:
 
const  menuItems = ref ([    { 		id : "1" , 		name : "首页" , 		router : this .id , 		subMenu : null , 	}, 	{ 		id : "2" , 		name : "消息" , 		router : this .id , 		subMenu : null , ]); 
 
3 无限发包 
什么getter,undefined
 
const  value = computed (props.value )const  value = computed (() =>  props.value )